#include "../src/lexer/lexercc.hpp"
#include "../src/parser/parsercc.hpp"
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <sstream>

using std::cin;
using std::cout;
using std::fstream;
using std::ifstream;
namespace fs = std::filesystem;

std::stringstream preprocessForHomework(istream &in);

/**
 * 这是一个完整的例子，从../testcase/里读取词法和文法文件，将生成的程序输出到
 * ../generated/文件夹中，并且会读取../testcase/midl.txt进行语法分析，将结果
 * 输出到../output/文件夹中
 */ 
int main() {
    cout << "\n\n";
    ifstream lexIn;
    ifstream grammarIn;
    ifstream astConfigIn;
    ifstream sourceIn;
    fstream grammarOut;
    fstream parseOut;
    fstream astJsonOut;
    fstream astTxtOut;
    if(!fs::exists("../output")){
        fs::create_directory("../output");
    }
    const string TEST_BASE = "../testcase/midl/";
    const string OUT_BASE = "../output/midl/";
    __OPEN_AND_CHECK(lexIn, TEST_BASE + "midl_lex.json", false);
    __OPEN_AND_CHECK(grammarIn, TEST_BASE + "midl_syntax.txt", false);
    __OPEN_AND_CHECK(astConfigIn, TEST_BASE + "midl_ast_conf.json", false);
    __OPEN_AND_CHECK(sourceIn, TEST_BASE + "midl.txt", false);
    __OPEN_AND_CHECK(grammarOut, OUT_BASE + "midl_table.csv", true);
    __OPEN_AND_CHECK(parseOut, OUT_BASE + "midl_parse_tree.txt", true);
    __OPEN_AND_CHECK(astJsonOut, OUT_BASE + "midl_AST.json", true);
    __OPEN_AND_CHECK(astTxtOut, OUT_BASE + "midl_AST.txt", true);
    // ---------------------------------------------------------------------- //
    ASTConfig config = ASTConfig::configWithJson(astConfigIn);
    // Lex 
    auto ssin = preprocessForHomework(sourceIn);
    MyLexer lexer = buildLexerFromJson(lexIn);
    auto tokens = lexer.lex(ssin);
    // Parse
    MyParser parser;
    clock_t st = clock();
    parser.buildParserFromSyntaxDef(grammarIn);
    parser.printParseTable(grammarOut);
    cout << "++++ Parse table saved at ./output/midl_table.csv\n";
    MyParser::PtrParseTreeNode parseTree;
    const string basePath = "../testcase/midl/testfiles/";
    vector<string> dirs = {"base_type_spec/", "declarators/", "expr/",
                           "member_list/",    "struct_type/", "type_spec/", ""};
    vector<vector<string>> files = {
        {"test9.txt", "test10_with_error.txt"},
        {"test11.txt", "test12_with_error.txt"},
        {"test13.txt", "test14_with_error.txt"},
        {"test3.txt", "test4_with_error.txt", "test5_with_error.txt"},
        {"test0.txt", "test1_with_error.txt", "test2_with_error.txt"},
        {"test6.txt", "test7_with_error.txt", "test8_with_error.txt"},
        {"test15_complex.txt"}};
    for (int i = 0; i < dirs.size(); ++i) {
        for (int j = 0; j < files[i].size(); ++j) {
            cout << basePath + dirs[i] + files[i][j] << endl;
            fstream sourceIn(basePath + dirs[i] + files[i][j]);
            fstream astOut("../output/midl/" + files[i][j],
                           fstream::trunc | fstream::out);
            vector<Token> tokens;
            MyParser::PtrParseTreeNode parseTree;
            try {
                tokens = lexer.lex(preprocessForHomework(sourceIn));
                parseTree = parser.parse(tokens);
            } catch (ParseException e) {
                astOut << "[Error] line " << e.linePos << "(" << e.charPos << "): \'"
                       << e.msg << "\'\nSyntax error!" << endl;
                cout << "[Error] line " << e.linePos << "(" << e.charPos << "): " << e.msg
                     << "\'\nSyntax error!" << endl;
                continue;
            }
            PtrASTNode ast = ASTNode::buildAST(parseTree, config);
            astOut << std::setw(2) << my_json_wrap(ast) << endl;
        }
    }
    // Generate codes
    // writeLexer(lexer, "../generated/", "../src/lexer", false);
    // writeParser(parser, "../generated/", "../src/parser", true);
    // ---------------------------------------------------------------------- //
    lexIn.close();
    grammarIn.close();
    astConfigIn.close();
    sourceIn.close();
    grammarOut.close();
    parseOut.close();
    astJsonOut.close();
    astTxtOut.close();
    system("pause");
}

std::stringstream preprocessForHomework(istream &in) {
    std::stringstream ss;
    string line;
    cin >> std::noskipws;
    auto lower = [](const string &s) {
        string ret;
        for (auto i : s) {
            ret.push_back(tolower(i));
        }
        return ret;
    };
    while (std::getline(in, line)) {
        ss << lower(line) << "\n";
    }
    return ss;
}